package com.typhoon.spring_shiro.interceptor;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.typhoon.spring_shiro.annotation.Logical;
import com.typhoon.spring_shiro.annotation.RequirePermission;
import com.typhoon.spring_shiro.constants.PermissionConstants;
import com.typhoon.spring_shiro.constants.PermissionEntityError;
import com.typhoon.spring_shiro.constants.SessionConstant;
import com.typhoon.spring_shiro.utils.CommonUtils;
import com.typhoon.spring_shiro.utils.CookieUtils;
import com.typhoon.spring_shiro.utils.RedisUtils;
import com.typhoon.spring_shiro.utils.ResponseUtils;
import com.typhoon.spring_shiro.utils.encrypt.Encrypt;

/**
 * 权限拦截器
 * 
 * @author Typhoon
 * @date 2017-06-05 20:20
 * @since V2.0
 */
public class PermissionInterceptor implements HandlerInterceptor {

	private static final Logger LOGGER = LoggerFactory.getLogger(PermissionInterceptor.class);

	protected static final String STATUS_KEY = "code";

	protected static final String DATA_KEY = "data";

	protected static final String MSG_KEY = "msg";

	@SuppressWarnings("unchecked")
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Method method = handlerMethod.getMethod();
		boolean isMethodAnnoted = method.isAnnotationPresent(RequirePermission.class);
		if (!isMethodAnnoted) {
			return true;
		}
		String sid = request.getParameter("sid");
		RequirePermission requirePermission = method.getAnnotation(RequirePermission.class);
		if (requirePermission == null) {
			Class<?> cls = handlerMethod.getBeanType();
			requirePermission = cls.getAnnotation(RequirePermission.class);
		}
		if (null == requirePermission || (ArrayUtils.isEmpty(requirePermission.value()))) {
			return true;
		}
		String[] permissions = requirePermission.value();
		Logical logical = requirePermission.logical();
		LOGGER.debug("权限码:{},逻辑关系:{}", ArrayUtils.toString(permissions), logical);
		boolean isAnd = false;
		if (Logical.AND.equals(logical)) {// 且
			isAnd = true;
		}

		if (sid == null) {// 必要要有sid
			sid = CookieUtils.getCookie(request, SessionConstant.SESSION_KEY);
			if (sid == null) {
				this.putParamResult(response, PermissionEntityError.ILLEGA_ACCESS_ERROR);
				return false;
			}
		}
		String decodeSid = Encrypt.decrypt(sid);
		LOGGER.info("decodeSid=" + decodeSid);
		int workerId = CommonUtils.evalInt(decodeSid.substring(decodeSid.indexOf("|") + 1, decodeSid.lastIndexOf("|")));
		LOGGER.debug("当前登录workerId:{}", workerId);
		Set<String> apps = RedisUtils.get(StringUtils.join(PermissionConstants.PERMISSION_CACHE_PREFIX, workerId, ":", PermissionConstants.APP_PRE), Set.class);
		Set<String> menus = RedisUtils.get(StringUtils.join(PermissionConstants.PERMISSION_CACHE_PREFIX, workerId, ":", PermissionConstants.MENU_PRE),
				Set.class);
		Set<String> actions = RedisUtils.get(StringUtils.join(PermissionConstants.PERMISSION_CACHE_PREFIX, workerId, ":", PermissionConstants.ACTION_PRE),
				Set.class);
		boolean isAollowed = false;
		String pm = null;
		if (isAnd) {
			isAollowed = true;
			for (int i = 0, length = permissions.length; i < length; i++) {
				pm = permissions[i];
				if (pm.startsWith(PermissionConstants.APP_PRE)) {
					if (!apps.contains(pm)) {
						isAollowed = false;
						break;
					}
				} else if (pm.startsWith(PermissionConstants.MENU_PRE)) {
					if (!menus.contains(pm)) {
						isAollowed = false;
					}
					break;
				} else if (pm.startsWith(PermissionConstants.ACTION_PRE)) {
					if (!actions.contains(pm)) {
						isAollowed = false;
						break;
					}
				}
			}
		} else {// 或者
			isAollowed = false;
			for (int i = 0, length = permissions.length; i < length; i++) {
				pm = permissions[i];
				if (pm.startsWith(PermissionConstants.APP_PRE)) {
					if (apps.contains(pm)) {
						isAollowed = true;
						break;
					}
				} else if (pm.startsWith(PermissionConstants.MENU_PRE)) {
					if (menus.contains(pm)) {
						isAollowed = true;
						break;
					}
				} else if (pm.startsWith(PermissionConstants.ACTION_PRE)) {
					if (actions.contains(pm)) {
						isAollowed = true;
						break;
					}
				}
			}
		}
		if (!isAollowed) {
			this.putParamResult(response, PermissionEntityError.UNAUTHORIZED_OP);
		}
		return isAollowed;
	}

	/**
	 * 组装返回参数
	 * 
	 * @author Typhoon
	 * @date 2017-06-05 20:21
	 * @since V2.0
	 * @param response
	 * @param entityError
	 */
	private void putParamResult(HttpServletResponse response, PermissionEntityError entityError) {
		Map<String, Object> results = new HashMap<String, Object>();
		results.put(STATUS_KEY, entityError.getCode());
		results.put(MSG_KEY, entityError.getMsg());
		ResponseUtils.renderJson(response, JSONObject.toJSONString(results, SerializerFeature.WriteMapNullValue));
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

	}

}
